#ifndef __CLIENT_CONNECTION_CONTROL_included
#define __CLIENT_CONNECTION_CONTROL_included

#include <vector>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include "Connection.h"
#include "ChannelUDP.h"
#include "RequestCheck.h"
#include "RequestRegister.h"
#include "Task.h"
#include "TaskSyncFully.h"
#include "../engine/Queue.h"
#include "../engine/DebugStream.h"
#include "../client-common/VersionTag.h"

class CConnectionControl : public CConnection {

	std::vector<ConnectionRef> mDataConnections;
	Engine::CQueue<TaskRef> mTasks;
	boost::shared_ptr<boost::thread> mTaskThread;
	boost::mutex mMutex;
	std::vector<VersionTagRef> mCheckedVersionTags;

	void TaskLoop() {
		
		while(!mClosed) {
			TaskRef t = mTasks.pop();
			try {
			if(t) t->Exec(boost::dynamic_pointer_cast<CConnection, IPackageReceiver>(shared_from_this()));
			}
			catch(std::exception& e) {
				Engine::DebugStream << "CConnectionControl::TaskLoop - " << e.what() << '\n';
			}
		}
	}

public :

	CConnectionControl(string& remote_addr, int remote_port, int local_port) :
		CConnection(remote_addr, remote_port, boost::shared_ptr<CChannel>(new CChannelUDP(local_port)))
	{ }
	
	virtual bool IsControlConnection() { return true; }
	virtual void Setup() { }

	void AddDataConnection(boost::shared_ptr<CConnection> conn) {
		boost::mutex::scoped_lock lock(mMutex);
		mDataConnections.push_back(conn);
	}

	void RemoveDataConnection(boost::shared_ptr<CConnection> conn) {

		boost::mutex::scoped_lock lock(mMutex);
		for(std::vector<boost::shared_ptr<CConnection>>::iterator i = mDataConnections.begin(); 
			i != mDataConnections.end(); i++) {
				if(i->get() == conn.get()) {
					mDataConnections.erase(i);
					break;
				}
		}
	}

	void AddCheckedVersionTag(VersionTagRef vt) {
		boost::mutex::scoped_lock lock(mMutex);
		mCheckedVersionTags.push_back(vt);
	}

	void Register() {
		
		Client::Common::CConfig& conf = Client::Common::CConfig::GetInstance();
		boost::shared_ptr<CRequestRegister> request(new CRequestRegister(conf.mAuthCode));
		conf.mAuthCode.clear();

		boost::shared_ptr<CResponse> response = SendRequest(request);
		if(!response || response->GetStatus() != RESPONSE_STATUS_OK) return;

		boost::shared_ptr<CPackage::JsonDocument> doc = response->ParseContentAsJson();
		if(doc->IsObject()) {
			if((*doc)["dev-id"].IsInt()) conf.mDeviceId = boost::lexical_cast<string>((*doc)["dev-id"].GetInt());
			if((*doc)["account"].IsString()) conf.mAccount = (*doc)["account"].GetString();
			if((*doc)["password"].IsString()) conf.mPassword = (*doc)["password"].GetString();
			
			conf.mFullSyncCompleted = false;
			conf.Save();

			mConfig.mDeviceId = conf.mDeviceId;
		}

	}

	void Check(bool full) {

		if(mConfig.mDeviceId.length() == 0) Register();
		if(mConfig.mDeviceId.length() == 0) return;

		if(!CConfig::GetInstance().mFullSyncCompleted && full) {
			AddTask(TaskRef(new CTaskSyncFully()));
			return;
		}

		string target;
		if(full) target = "all-tags";
		else target = "tags";
		boost::shared_ptr<CRequestCheck> request(new CRequestCheck(target));
		

		{
			boost::mutex::scoped_lock lock(mMutex);
			if(mCheckedVersionTags.size() > 0) {
				Client::Common::TableRef tvt = Client::Common::CVersionTag::GetSerializedTable();
				while(mCheckedVersionTags.size() > 0) {
					std::vector<VersionTagRef>::iterator i = mCheckedVersionTags.begin();
					tvt->Add(*i);
					mCheckedVersionTags.erase(i);
				}

				request->SetContent(tvt->ToString(), string(PACKAGE_CONTENT_TYPE_JSON));
				request->AddParameter(string("with-checked"), string("true"));
			}
		}

		boost::shared_ptr<CResponse> response = SendRequest(request);
		if(response && response->GetStatus() == RESPONSE_STATUS_OK) {
			request->SetConnection(boost::dynamic_pointer_cast<CConnection, IPackageReceiver>(shared_from_this()));
			request->HandleResponse(response);
		}
	}

	void ApplyAuthCode(string& authcode) {

		RequestRef request(new CRequestRegister());
		request->AddParameter(string("add-device"), string("true"));

		boost::shared_ptr<CResponse> response = SendRequest(request);
		if(response && response->GetStatus() == RESPONSE_STATUS_OK) {
			boost::shared_ptr<CPackage::JsonDocument> doc = response->ParseContentAsJson();
			if(doc->IsObject()) {
				CPackage::JsonValue& v = (*doc)["authcode"];
				if(v.IsString()) authcode = v.GetString();
			}
		}
	}

	virtual void AddTask(TaskRef t) {
		
		if(!mTaskThread) mTaskThread = 
			boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CConnectionControl::TaskLoop, this)));

		mTasks.push(t);
	}

	virtual void Close() {
		CConnection::Close();
		if(mTaskThread) mTaskThread->interrupt();
	}
};

typedef boost::shared_ptr<CConnectionControl> ConnectionControlRef;

#endif // __CLIENT_CONNECTION_CONTROL_included